package com.atguigu.stream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import org.apache.shiro.codec.Hex;
import org.junit.Test;

import com.atguigu.lambda.Employee;
import com.atguigu.lambda.EmployeeData;

import static java.lang.Integer.toHexString;

/**
 * 测试中间操作
 * 1.可以通过Stream的实例，执行多次中间操作
 * 2.中间操作，只有在执行了终止操作以后才会执行。
 *
 *
 *
 * Created by shkstart on 2017/8/29 0029.
 */
public class StreamAPITest1 {

    //3-排序
    @Test
    public void test4(){
//        sorted()——自然排序
        List<Integer> list = Arrays.asList(23,43,454,32,1,2,5,5,-8);
        list.stream().sorted().forEach(System.out::println);

        //此时针对Employees进行排序：失败。原因：Employee类没有实现Comparable接口
//        List<Employee> list1 = EmployeeData.getEmployees();
//        list1.stream().sorted().forEach(System.out::println);

//        sorted(Comparator com)——定制排序
        List<Employee> list1 = EmployeeData.getEmployees();
       
        
        list1.stream().sorted((e1,e2) -> {
            if(e1.getAge() != e2.getAge()){
                return e1.getAge() - e2.getAge();
            }else{
                return -Double.compare(e1.getSalary(),e2.getSalary());
            }
        }).forEach(System.out::println);

    }

    @Test
    public void test3(){
        ArrayList list1 = new ArrayList();
        list1.add(1);
        list1.add(2);
        list1.add(3);

        ArrayList<Integer> list2 = new ArrayList();
        list2.add(4);
        list2.add(5);
        list2.add(6);

        list2.removeIf(e -> e < 5);

        list2.forEach(System.out::println);

//        list1.add(list2);
//        System.out.println(list1);//[1, 2, 3, [4, 5, 6]]

//        list1.addAll(list2);
//        System.out.println(list1);//[1, 2, 3, 4, 5, 6]
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String t = "2025-01-06 20:41:37";
        LocalDateTime lt = LocalDateTime.parse(t,formatter);
        System.out.println(lt);
    }
    //2-映射
    @Test
    public void test2(){
//        map(Function f)——接收一个函数作为参数，将元素转换成其他形式或提取信息，该函数会被应用到每个元素上，并将其映射成一个新的元素。
        List<String> list = Arrays.asList("aa","bb","cc","dd");
        list.stream().map(String::toUpperCase).forEach(System.out::println);
//        练习：获取员工姓名长度大于3的员工的姓名。
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        Stream<String> stream1 = stream.map(Employee::getName);
        stream1.filter(name -> name.length() > 3).forEach(System.out::println);


        Stream<Stream<Character>> stream2 = list.stream().map(StreamAPITest1 :: fromStringToChar);
        stream2.forEach(
        		x -> {
        			x.forEach(System.out::println);
        		}
        		);

        System.out.println();

//        flatMap(Function f)——接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
        Stream<Character> stream3 = list.stream().flatMap(StreamAPITest1::fromStringToChar);
        stream3.forEach(System.out::println);
    }
    //将str中的字符存在集合中，返回集合的Stream
    public static Stream<Character> fromStringToChar(String str){
        ArrayList<Character> list = new ArrayList<>();
//        for(Character c : str.toCharArray()){
//              list.add(c);
//        }
        for(int i = 0;i < str.length();i++){
            list.add(str.charAt(i));
        }
        return list.stream();
    }


    //1-筛选与切片
    @Test
    public void test1(){
        Long a = System.currentTimeMillis() / 1000 + 20 * 365 * 24 * 60 * 60;
        System.out.println(a);
        List<Employee> list = EmployeeData.getEmployees();

        list.removeIf(e -> e.getAge() < 35);
        list.forEach(System.out::println);
//        filter(Predicate p)——接收 Lambda ， 从流中排除某些元素。
        Stream<Employee> stream = list.stream().filter(e -> e.getAge() > 30);
        stream.forEach(System.out::println);

        System.out.println();

//        limit(n)——截断流，使其元素不超过给定数量。
        list.stream().filter(e -> e.getAge() > 30).limit(3).forEach(System.out::println);

        System.out.println();

        //skip(n) —— 跳过元素，返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个，则返回一个空流。与 limit(n) 互补
        list.stream().filter(e -> e.getAge() > 30).skip(3).forEach(System.out::println);

        System.out.println();

        //distinct()——筛选，通过流所生成元素的 hashCode() 和 equals() 去除重复元素
        list.add(new Employee(1009,"刘强东",30,6000));
        list.add(new Employee(1009,"刘强东",30,6000));
        list.add(new Employee(1009,"刘强东",30,6000));
        list.add(new Employee(1009,"刘强东",30,6000));
        list.add(new Employee(1009,"刘强东",30,6000));
        list.stream().distinct().forEach(System.out::println);
        
    }

    @Test
    public void testMax() {
        //List<Employee> list = EmployeeData.getEmployees();
        List<Employee> list = new ArrayList<>();
        //final Employee[] maxEmployee = {null};
        Optional<Employee> a = list.stream().max(Comparator.comparingInt(Employee::getAge));
//        a.ifPresent(v -> {
//            maxEmployee[0] = v;
//        });
        //System.out.println(maxEmployee[0]);
        if(a.isPresent()) {
            System.out.println(a.get());
        }

        //LocalDate.parse("2025-07-12 00:00:00");

        Path p = Paths.get("/user/001/web/mapOperationTask");
        System.out.println(p.getParent());
        System.out.println(p.getFileName());
        System.out.println(p.getRoot());
        System.out.println(p.subpath(1,2));
        System.out.println(p.subpath(2,4));

        long timestamp = 1733457604189L;
        Instant instant = Instant.ofEpochMilli(timestamp);

        // 设置时区为中国标准时间 (CST)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                .withZone(ZoneId.of("Asia/Shanghai"));

        System.out.println(formatter.format(instant));
    }

    @Test
    public void testBase64() throws Exception {
        JSONObject response = new JSONObject();
        response.put("bindtype", "00100000");
        response.put("pointName", "pointname");
        response.put("pointName1", "pointname");
        response.put("pointName2", "pointname");
        response.put("pointName3", "pointname");
        String jsonString = response.toJSONString(JSONWriter.Feature.MapSortField);
        System.out.println(jsonString);
        String base64Encoded  = encode(jsonString);
        //String base64Encoded = Base64.getEncoder().encodeToString();
        System.out.println(base64Encoded);

        String decoded = decode(base64Encoded);
        System.out.println(decoded);
    }

    public static String encode(String input) throws Exception {
        // Compress the input string to reduce its length before encoding.
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
            gzipOutputStream.write(input.getBytes(StandardCharsets.UTF_8));
        }

        // Encode the compressed byte array to a Base64 string.
        return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }

    public static String decode(String encoded) throws Exception {
        // Decode the Base64 string to a byte array.
        byte[] decodedBytes = Base64.getDecoder().decode(encoded);

        // Decompress the byte array back to the original string.
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decodedBytes);
        try (GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream)) {
            return new String(gzipInputStream.readAllBytes(), StandardCharsets.UTF_8).trim();
        }
    }

    @Test
    public void testHash() {
        JSONObject response = new JSONObject();
        response.put("bindtype", "00100000");
        response.put("pointName", "pointname");
        response.put("pointName1", "pointname");
        response.put("pointName2", "pointname");
        response.put("pointName3", "pointname");
        String data = response.toJSONString(JSONWriter.Feature.MapSortField);
        System.out.println("Original: " + data);

        String hashed = hash(data);
        System.out.println("SHA-256 Hashed: " + hashed);
        response = new JSONObject();
        response.put("bindtype", "00100000");
        response.put("pointName", "pointname");
        response.put("pointName1", "pointname");
        response.put("pointName2", "pointname");
        response.put("pointName3", "pointname1");
        String data1 = response.toJSONString(JSONWriter.Feature.MapSortField);
        System.out.println("Original: " + data1);

        String hashed1 = hash(data1);
        System.out.println("SHA-256 Hashed: " + hashed1);

        response = new JSONObject();
        response.put("bindtype", "00100000");
        response.put("pointName", "pointname");
        response.put("pointName1", "pointname");
        response.put("pointName2", "pointname");
        response.put("pointName3", "pointname1");
        String data2 = response.toJSONString(JSONWriter.Feature.MapSortField);
        System.out.println("Original: " + data2);

        String hashed2 = hash(data2);
        System.out.println("SHA-256 Hashed: " + hashed2);
    }

    public static String hash(String input) {
        try {
            //MessageDigest digest = MessageDigest.getInstance("MD5");
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] encodedhash = digest.digest(input.getBytes(java.nio.charset.StandardCharsets.UTF_8));

            // Convert byte array into signum representation
            StringBuilder hexString = new StringBuilder(2 * encodedhash.length);
            for (byte b : encodedhash) {
                String hex = Integer.toHexString(0xff & b);
                if(hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void testCmdFmAngle() {
        Map<String,String> resultMap = cmdFmAngle("0000000000000000", 100);
        System.out.println(resultMap);
    }

    public static Map<String,String>  cmdFmAngle(String code, int angle) {
        Map<String,String> resultMap = new HashMap<>();
        int loc = 2;
        byte cs = 0;
        String strCmdAngle = "68C08669710390347690010403133201A316";

        byte[] fmcode = Hex.decode(code);
        System.out.printf("设置阀门开度指令：%s, 开度:%d\r\n", code, angle);

        if(code.length() != 16) {
            resultMap.put("code","500");
            resultMap.put("msg","Fm code len err!!");
            return resultMap;
        }

        if(angle > 100) {
            angle = 100;
        }else if(angle < 0){
            angle = 0;
        }

        byte[] cmd = Hex.decode(strCmdAngle);

        // fm code
        for(int i = 0; i < 8; i++){
            cmd[i+loc] = fmcode[i];
        }
        // fm angle
        cmd[14] = (byte) angle;

        // cs
        for(int i = 0;i < cmd.length; i ++){
            if(i<(cmd.length-2)) {
                cs += cmd[i];
            }
        }
        cmd[cmd.length-2] = cs;

        // cmd[] 为阀门指令序列，可直接发送到平台
        System.out.printf("指令码：");
        for(int i = 0;i < cmd.length; i ++){
            System.out.printf("%02x", cmd[i]);
        }

        System.out.printf("\r\n\r\n");

        StringBuffer sb = new StringBuffer();
        for(int i = 0;i < cmd.length; i ++){
            String hex = toHexString(cmd[i] & 0xFF);
            if(hex.length() < 2){
                sb.append(0);
            }
            sb.append(hex);
        }
        System.out.println(sb.toString());
        String str=sb.toString().toUpperCase();
        resultMap.put("code","200");
        resultMap.put("msg",str);
        return  resultMap;
    }

    @Test
    public void testbit() {
        //Integer stateCode = 26;
        Integer stateCode = 32768;
        System.out.println(stateCode & (1 << 14));
    }
}
